package com.common.redis.limit;

import com.common.core.exception.ExceptionFactory;
import com.common.core.exception.code.LimitErrorCode;
import com.common.redis.annotation.RedisLimit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Aspect
@Component
public class RedisLimitConfig {

    @Resource
    private RedissonClient redissonClient;

    @Pointcut(value = "@annotation(com.common.redis.annotation.RedisLimit)")
    public void redisLimitAnnotationPointcut() {
    }

    @Around(value = "redisLimitAnnotationPointcut()")
    public Object invokeResourceWithRedisLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        RedisLimit[] annotationsByType = method.getAnnotationsByType(RedisLimit.class);
        if (null == annotationsByType || annotationsByType.length == 0) {
            return joinPoint.proceed();
        }
        RedisLimit redisLimit = annotationsByType[0];
        // 声明一个限流器
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(redisLimit.key());
        //设置速率，time秒中产生count个令牌
        rateLimiter.trySetRate(RateType.OVERALL, redisLimit.count(), redisLimit.time(), RateIntervalUnit.MILLISECONDS);
        // 试图获取一个令牌，获取到返回true
        boolean tryAcquire = rateLimiter.tryAcquire();
        if (!tryAcquire) {
            throw ExceptionFactory.sysException(LimitErrorCode.A_API_LIMIT_ERROR);
        }
        return joinPoint.proceed();
    }
}